import colander
import deform
from deform.widget import TextAreaWidget, MappingWidget, \
     TextInputWidget, HiddenWidget, RadioChoiceWidget

from ampersands.core import vocabulary
from ampersands.core.widgets import SequenceBrowserWidget, \
     SequenceAutocomplete, HiddenMappingWidget, IFrameWidget, \
     LicenseCheckboxWidget, PlainTextWidget, SelectWidget


class Identifier(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.IDENTIFIER_TYPE_KEYS))
    value = colander.SchemaNode(colander.String())


class Identifiers(colander.SequenceSchema):
    identifier = Identifier()


class NamePart(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.NAME_PART_TYPE_KEYS))
    value = colander.SchemaNode(colander.String())


class NameParts(colander.SequenceSchema):
    namePart = NamePart()


class Name(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.NAME_TYPE_KEYS))
    dateFrom = colander.SchemaNode(colander.DateTime())
    dateTo = colander.SchemaNode(colander.DateTime())
    nameParts = NameParts()


class Names(colander.SequenceSchema):
    name = Name()


class Arg(colander.MappingSchema):
    required = colander.SchemaNode(colander.String())
    type = colander.SchemaNode(colander.String())
    use = colander.SchemaNode(colander.String())
    value = colander.SchemaNode(colander.String())


class Args(colander.SequenceSchema):
    arg = Arg()


class ElectronicAddress(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.ELECTRONIC_ADDRESS_TYPE_KEYS))
    args = Args()
    value = colander.SchemaNode(colander.String())


class ElectronicAddresses(colander.SequenceSchema):
    electronicAddress = ElectronicAddress()


class PhysicalAddressPart(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.ADDRESS_PART_TYPE_KEYS))
    value = colander.SchemaNode(colander.String())


class PhysicalAddressParts(colander.SequenceSchema):
    part = PhysicalAddressPart()


class PhysicalAddress(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.PHYSICAL_ADDRESS_TYPE_KEYS))
    addressPart = PhysicalAddressParts()


class PhysicalAddresses(colander.SequenceSchema):
    physicalAddress = PhysicalAddress()


class Spatial(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.SPATIAL_TYPE_KEYS))
    value = colander.SchemaNode(colander.String())


class Spatials(colander.SequenceSchema):
    spatial = Spatial()


class Address(colander.MappingSchema):
    electronic = ElectronicAddresses()
    physical = PhysicalAddresses()


class Addresses(colander.SequenceSchema):
    address = Address()


class Location(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.LOCATION_TYPE_KEYS))
    dateFrom = colander.SchemaNode(colander.DateTime(),
                                   widget=deform.widget.DateInputWidget())
    dateTo = colander.SchemaNode(colander.DateTime(),
                                 widget=deform.widget.DateInputWidget())
    addresses = Addresses()
    spatials = Spatials()


class Locations(colander.SequenceSchema):
    location = Location()


class Relation(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                               validator=colander.OneOf(vocabulary.COLLECTION_RELATIONSHIP_TYPE_KEYS))
    url = colander.SchemaNode(colander.String())
    description = colander.SchemaNode(colander.String())


class Relations(colander.SequenceSchema):
    relation = Relation()


class RelatedObject(colander.MappingSchema):
    key = colander.SchemaNode(colander.String())
    relations = Relations()


class RelatedObjects(colander.SequenceSchema):
    relatedObject = RelatedObject()


class Subject(colander.MappingSchema):
    label = colander.SchemaNode(colander.String(),
                               widget=TextInputWidget(
                                    template="readonly/autocomplete_label",
                                    category="structural"),
                                missing='')
    type = colander.SchemaNode(colander.String(),
                               widget=HiddenWidget())
    value = colander.SchemaNode(colander.String(),
                                widget=HiddenWidget())


class Subjects(colander.SequenceSchema):
    subject = Subject(widget=HiddenMappingWidget(), title="")


class Description(colander.MappingSchema):
    type = colander.SchemaNode(colander.String(),
                    validator=colander.OneOf(vocabulary.DESCRIPTION_TYPE_KEYS))
    value = colander.SchemaNode(colander.String())


class Descriptions(colander.SequenceSchema):
    description = Description()


class Datasets(colander.SequenceSchema):
    dataset = colander.SchemaNode(colander.String())


class Collection(colander.MappingSchema):
    originatingSource = colander.SchemaNode(colander.String())
    type = colander.SchemaNode(colander.String(), missing=None,
                               validator=colander.OneOf(vocabulary.COLLECTION_TYPES_KEYS))
    dateModified = colander.SchemaNode(colander.DateTime(), missing=None)
    dateAccessioned = colander.SchemaNode(colander.DateTime(), missing=None)
    descriptions = Descriptions()
    subjects = Subjects()
    identifiers = Identifiers()
    names = Names()
    locations = Locations()
    relatedObjects = RelatedObjects()
    datasets = Datasets()


class LicenseAgreement(object):
    """ Validator which succeeds if the value passed to it is True."""
    def __call__(self, node, value):

        if not value:
            err = 'You must agree to the license before continuing.'
            raise colander.Invalid(node, err)


def cc_license_form(context=None, request=None):
    license_class = ""
    data = {}
    if request.POST and request.POST.get('license_class'):
        license_class = request.POST['license_class']
        if license_class != "custom":
            data = vocabulary.cc_getLicense(request.POST['license_class'])
    elif context.getData('rights').get("license_class"):
        license_class = context.getData('rights').get('license_class')
        if license_class != "custom":
            data = vocabulary.cc_getLicense(
                context.getData('rights').get('license_class'))

    r = colander.MappingSchema()
    r.add(colander.SchemaNode(
        colander.String(), name="license_class",
        default=license_class,
        widget=SelectWidget(template="cc_select",
                            values=vocabulary.cc_getLicenseClasses())))

    if license_class == "zero":
        r.add(colander.SchemaNode(
            colander.Boolean(), name="copyright_waiver",
            title="",
            validator=LicenseAgreement(),
            description="I hereby waive all copyright and related or"
            " neighboring rights together with all associated claims"
            " and causes of action with respect to this work to the"
            " extent possible under the law.",
            # hidden in this case only hides the required *
            widget=LicenseCheckboxWidget()))

        r.add(colander.SchemaNode(
            colander.String(), missing=u'', name="disclaimer_text",
            title="", widget=IFrameWidget(url="cc-zero.html")))

        r.add(colander.SchemaNode(
            colander.Boolean(), name="copyright_agreement",
            title="",
            validator=LicenseAgreement(),
            description="I have read and understand the terms and"
            " intended legal effect of CC0, and hereby voluntarily"
            " elect to apply it to this work.",
            # hidden in this case only hides the required *
            widget=LicenseCheckboxWidget()))
    elif license_class == "custom":
        r.add(colander.SchemaNode(
            colander.String(), name="license_text",
            title="License Text",
            widget=TextAreaWidget(rows=5, cols=60)))
    else:
        for k in data.get('__keys__', []):
            widget = data[k]
            if widget['type'] == 'enum':
                if len(widget['enum']) < 5:
                    Widget = RadioChoiceWidget
                else:
                    Widget = SelectWidget

                r.add(colander.SchemaNode(
                    colander.String(),
                    name=k,
                    title=widget['label'],
                    description=widget['description'],
                    widget=Widget(values=widget['enum'].items())))
    return r


def render_license(license_data, user=None, title=None):
    """this function takes a dictionary of license information"""
    if license_data.get('license_class', '') in ['standard', 'recombo']:
        l = vocabulary.cc_renderLicense(license_data)
        l["rights"] = l["license-name"]
        return l
    elif license_data.get('license_class', '') == "zero":
        return {"rights": "To the extent possible under law, %s has "
                "waived all copyright and related or neighboring rights"
                " to %s. This work is published from: "
                "Australia." % (user, title)}
    else:
        return {"rights": license_data.get("license_text", "")}


class ReadonlyLicense(colander.MappingSchema):
    rights = colander.SchemaNode(colander.String())


##
## Street Address
##

class StreetAddress(colander.MappingSchema):
    addressLine1 = colander.SchemaNode(colander.String(),
                                       title="Address Line 1",
                                       widget=TextInputWidget(size=61))
    addressLine2 = colander.SchemaNode(colander.String(),
                                       title="Address Line 2",
                                       missing="",
                                       widget=TextInputWidget(size=61))
    suburbOrPlaceOrLocality = colander.SchemaNode(
        colander.String(), title="Suburb, Place or Locality",
        widget=TextInputWidget(size=61))
    stateOrTerritory = colander.SchemaNode(colander.String(),
                                           title="State or Territory",
                                           widget=TextInputWidget(size=61))
    postCode = colander.SchemaNode(colander.Int(),
                                   title="Post Code")


##
## Split Form
##


class Collection(colander.Schema):
    identifier = colander.SchemaNode(colander.String(), missing=None)
    dateModified = colander.SchemaNode(colander.DateTime(), missing=None)
    dateAccessioned = colander.SchemaNode(colander.DateTime(), missing=None)


class Basic(colander.MappingSchema):
    title = colander.SchemaNode(colander.String(),
                                widget=TextInputWidget(size=61))
    description = colander.SchemaNode(colander.String(), missing=u'',
                                      widget=PlainTextWidget(rows=10, cols=60))
    subjects = Subjects(validator=colander.Length(1),
                        widget=SequenceAutocomplete(size=61,
                                            values="/autocomplete_subject"))


class DataSources(colander.MappingSchema):
    datasets = Datasets(widget=SequenceBrowserWidget())


class Locations(colander.MappingSchema):
    streetAddress = StreetAddress(title="Street Address")


class Activities(colander.MappingSchema):
    activities = colander.SchemaNode(colander.String(), missing=u'',)


class Parties(colander.MappingSchema):
    parties = colander.SchemaNode(colander.String(), missing=u'',)


class Collections(colander.MappingSchema):
    collections = colander.SchemaNode(colander.String(), missing=u'',)


class Collection(colander.Schema):
    identifier = colander.SchemaNode(colander.String(), missing=None)
    dateModified = colander.SchemaNode(colander.DateTime(), missing=None)
    dateAccessioned = colander.SchemaNode(colander.DateTime(), missing=None)
    type = colander.SchemaNode(colander.String(), missing="collection")
    basic = Basic(title="Basic", widget=MappingWidget())
    rights = ReadonlyLicense(title="Rights", widget=MappingWidget())
    dataSources = DataSources(title="Data Sources", widget=MappingWidget())
    location = Locations(title="Locations", widget=MappingWidget())
    activities = Activities(title="Activities", widget=MappingWidget())
    parties = Parties(title="Parties", widget=MappingWidget())
    collections = Collections(title="Collections", widget=MappingWidget())
